﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using OpenLS.Drawing;
using OpenLS.Spreadsheet.Formats.Biff.Records;
using OpenLS.Spreadsheet.Internals;

namespace OpenLS.Spreadsheet.Formats.Biff
{
    class BiffWriterWorkbookContext
    {
        readonly List<XFRecord> _xfRecords = new List<XFRecord>();
        internal List<XFRecord> XFRecords { get { return _xfRecords; } }

        private readonly Workbook _workbook;
        private readonly BinaryWriter _writer;
        Dictionary<DefinedName, uint> DefinedNamesToId = new Dictionary<DefinedName, uint>();
        Dictionary<string, uint> DefinedNameNamesToId = new Dictionary<string, uint>();
        public void RegisterDefinedName(DefinedName definedName)
        {
            this.DefinedNamesToId.Add(definedName, (uint)(LblCount + 1)); //\\ one based
        }

        protected int LblCount  
        {
            get { return DefinedNamesToId.Count + DefinedNameNamesToId.Count; }
        }

        public void RegisterDefinedName(string definedName)
        {
            this.DefinedNameNamesToId.Add(definedName, (uint)(LblCount+ 1)); //\\ one based
        }
        public uint GetDefinedNameId(DefinedName definedName)
        {
            Contract.Requires(definedName != null);
            Contract.Ensures(Contract.Result<uint>() > 0);
            return DefinedNamesToId[definedName];
        }
        public uint GetDefinedNameId(string definedName)
        {
            Contract.Requires(definedName != null);
            Contract.Ensures(Contract.Result<uint>() > 0);
            return DefinedNameNamesToId[definedName];
        }


        public BiffWriterWorkbookContext(Workbook workbook, BinaryWriter writer)
        {
            _workbook = workbook;
            _writer = writer;
#if DEBUG
            Current = this;
#endif
        }

        public BinaryWriter Writer
        {
            get { return _writer; }
        }

        public Workbook Workbook
        {
            get { return _workbook; }
        }

        public long Position
        {
            get { return Writer.BaseStream.Position; }
            set { Writer.BaseStream.Position = value; }
        }

        Dictionary<string, ushort> codeToId = new Dictionary<string, ushort>();

        public ushort GetExistingCodeId(string code)
        {
            var i = StandardFormats.GetFormatId(code);
            if (i != null)
                return (ushort)i.Value;
            ushort result;
            if (!codeToId.TryGetValue(code, out result))
            {
                Debug.WriteLine("Couldnt find " + code);
                // Contract.Assert(false);
            }
            return result;
        }
        public ushort EnsureCodeId(string code)
        {
            var i = StandardFormats.GetFormatId(code);
            if (i != null)
                return (ushort) i.Value;
            ushort result;
            if (!codeToId.TryGetValue(code, out result))
            {
                int key = (170 + codeToId.Count);
                result = (ushort)key;
                codeToId.Add(code, result);
            }
            return result;
        }

        private Palette builtinPalette = new BuiltinPalette();
        public IcvXF GetColor (SpreadsheetColor color, IcvXF nullValue)
        {
            IndexedColor ic = color as IndexedColor;
            if (ic != null)
                return (IcvXF) ic.Index;
            return nullValue;
            if (color == null)
                return nullValue;
            return (IcvXF) builtinPalette.GetClosestIndex(color.GetColor(this.ColorScheme)); //\\hmm
            
        }

        protected ColorScheme ColorScheme
        {
            get
            {
                if (Theme != null)
                    return Theme.ColorScheme;
                return null;
            }
        }

        protected Theme Theme
        {
            get { return Workbook.Theme; }
        }

        public int CommentsCount    
        {
            get { return this._commentToObj.Count; }
        }

        readonly Dictionary<Font, ushort> _fontTable = new Dictionary<Font, ushort>();
        public ushort GetFontId(Font font)
        {
#if DEBUG
            if (!_fontTable.ContainsKey (font))
            {
                Debug.WriteLine(string.Format("{0} {1}", font, font.GetHashCode()));
                foreach (var f in _fontTable.Keys)
                {
                    Debug.WriteLine(string.Format("{0} {1}", f.ToString(), f.GetHashCode()));
                    Debug.WriteLine(f.Equals(font));
                }
            }
#endif
            return _fontTable[font];
        }

        private ushort fontCount = 0;
        public void AddFont(Font font)
        {

            if (fontCount == 4)
                fontCount++;
            _fontTable.Add(font, fontCount) ;
            fontCount++;
        }
        public readonly Dictionary<StyleFormattingRecord, ushort> StyleStyleRecordTable = new Dictionary<StyleFormattingRecord, ushort>();
        public readonly Dictionary<CellFormattingRecord, ushort> CellStyleRecordTable = new Dictionary<CellFormattingRecord, ushort>();

        public void WriteUInt16(ushort value)
        {
            Writer.Write(value);
        }

        public void WriteSByte(sbyte value)
        {
            Writer.Write(value);
        }

        public void WriteUInt32(uint value)
        {
            Writer.Write(value);
        }

        public void Write(uint value)
        {
            Writer.Write(value);
        }
        public void Write(ushort value)
        {
            Writer.Write(value);
        }
        public void Write(short value)
        {
            Writer.Write(value);
        }
        public void Write(int value)
        {
            Writer.Write(value);
        }
        public void Write(sbyte value)
        {
            Writer.Write(value);
        }
        public void Write(byte value)
        {
            Writer.Write(value);
        }
        public void Write(byte[] value)
        {
            Contract.Requires(value != null);
            Writer.Write(value);
        }
        public void Write(double value)
        {
            Writer.Write(value);
        }
        public void Write(float value)
        {
            Writer.Write(value);
        }
        public readonly Dictionary<BoundSheetRecord, long> SheetToPosition = new Dictionary<BoundSheetRecord, long>();

        public MovedContext CreateMovedContext(long newPosition)
        {
            MovedContext result = new MovedContext(this, Position);
            Position = newPosition;
            return result;
        }

        public long DefColumnWidthInIndex;
        public Dictionary<Sheet, BoundSheetRecord> SheetToBoundSheet = new Dictionary<Sheet, BoundSheetRecord>();
        public long? DBCellPosition;

        public void AddFormattingRecord(FormattingRecord formattingRecord)
        {
            Contract.Requires(formattingRecord != null);
            ushort newCount = (ushort)(this.StyleStyleRecordTable.Count + CellStyleRecordTable.Count);
            if (formattingRecord is CellFormattingRecord)
                    CellStyleRecordTable.Add((CellFormattingRecord) formattingRecord, newCount);
            else
                StyleStyleRecordTable.Add((StyleFormattingRecord) formattingRecord, newCount);
        }

        public int GetSstId(string value)
        {

            return StringTable[(XLUnicodeRichExtendedString) value];
        }
        public Dictionary<XLUnicodeRichExtendedString, int> StringTable = new Dictionary<XLUnicodeRichExtendedString, int>();
#if DEBUG
        public List<FormattingRecord> cellRecords = new List<FormattingRecord>();
        public List<FormattingRecord> styleRecords = new List<FormattingRecord>();
        public List<FormattingRecord> GetRecords (XFRecordType b)
        {
            switch (b)
            {
                    case XFRecordType.Cell:
                    return cellRecords;
                    case XFRecordType.Style:
                    return styleRecords;
                default:
                    throw new NotSupportedException();
            }
        }
#endif

        public ushort GetSheetIndex(Worksheet worksheet)
        {
            return (ushort) worksheet.Workbook.Sheets.IndexOf(worksheet); //\\
        }

        Dictionary<Comment, ObjRecord> _commentToObj = new Dictionary<Comment, ObjRecord>();
        Dictionary<ObjRecord, ushort> _ObjRecordToId = new Dictionary<ObjRecord, ushort>(); 
        public ushort GetObjIdFromComment(Comment comment)
        {
            ObjRecord objRecord = _commentToObj[comment];
            return _ObjRecordToId[objRecord];
        }

        public void AddCommentToObject(Comment key, ObjRecord value)
        {
            _commentToObj.Add(key, value);
            AddObjRecord(value);
            //\\_ObjRecordToId.Add(value, (ushort) (_ObjRecordToId.Count + 1));
        }

        public ushort GetObjIdFromObj(ObjRecord objRecord)
        {
            return _ObjRecordToId[objRecord];
        }

        readonly Dictionary<Blip, int> _blipTable = new Dictionary<Blip, int>();
        public PictureContext PictureContext = new PictureContext();
#if DEBUG
        public static BiffWriterWorkbookContext Current;
        public bool HasSheetId(ushort sheetId)
        {
            return true;
        }
#endif
        public void AddBlip(Blip blip)
        {
            _blipTable.Add(blip, _blipTable.Count + 1); // 1 based;
        }

        public int GetBlipId(Blip blip)
        {
            return _blipTable[blip];
        }

        public void AddObjRecord(ObjRecord record)
        {
            _ObjRecordToId.Add(record, (ushort) (_ObjRecordToId.Count+1));
        }

        internal void AddCodeId(string code, ushort id)
        {
            this.codeToId.Add(code, id);
        }

        
    }
}